<?php
/*
 * ds.php
 *
 * Script del Data Source.
 *
 * Controllo del servizio richiesto:
 *
 * - se e' una query, la trasforma in un xpath, lo esegue sull'indice e
 * ritorna l'elenco dei risultati.
 * - se e' un salvataggio scheda, inserisce nel DB e crea il file xml.
 *
 * Cataloghi e schede vengono serviti direttamente da apache.
 */

/*
 * Variabili globali.
 */

$META_INDEX = $SCRIPT_DIR . '/ds/index.xml';


/*
 * Esecuzione.
 */

if (is_query_request ($PATH))
	execute_query ();
else if (is_save_request ($PATH))
	save_expression ($PATH);
else
	echo DSError ("404", "La risorsa richiesta è inesistente");


/*
 * Funzioni.
 */

function is_query_request ($path)
/*
 * Ritorna true se $path e' quello per fare le query.
 */
{
	global $DS_URI;
	if ($path == $DS_URI . "/search")
		return true;
	return false;
}


function is_save_request ($path)
/*
 * Ritorna true se $path e' quello per salvare una scheda.
 */
{
	global $DS_URI;
	if ($path == $DS_URI . "/save")
		return true;
	return false;
}


function execute_query ()
{
	global $DEBUG;
	global $HOST;
	global $PATH;
	global $QUERY;
	global $META_INDEX;

	$dc_all = array (
		"widentifier" => null,
		"wcreator" => null,
		"wcoverage" => null,
		"wtitle" => null,
		"wdate" => null,
		"eidentifier" => null,
		"ecreator" => null,
		"econtributor" => null,
		"edate" => null,
		"edescription" => null,
		"elanguage" => null,
		"erelation" => null,
		"esource" => null,
		"epublisher" => null,
		"folksonomia" => null,
		"etitle" => null,
		"etype" => null
	);

	$url2xpath = '//metadati';

	/*
	 * Query senza parametri viene considerata un errore.
	 */
	if (count ($_GET) == 0) {
		echo DSError ("400", "Query mal formata: parametri assenti.");
		die ();
	}

	/*
	 * Itera sui parametri della query e inserisce i valori dei parametri
	 * nell'array $dc_all, sotto la chiave corrispondente.
	 */
	foreach ($_GET as $dc_key => &$dc_val) {
		/*
		 * Se il parametro $dc_key non e' un elemento Doublin Core valido (e.g. ?joe=...)
		 * la query e' malformata.
		 */
		if (!array_key_exists ($dc_key, $dc_all) || $dc_all[$dc_key] != null) {
			echo DSError("400", "Query mal formata: {$dc_key} non e' un parametro valido.");
			die ();
		}

		$dc_all[$dc_key] = $dc_val;
	}

	/*
	 * Costruzione $url2xpath.
	 */
	foreach ($dc_all as $dc_key => $dc_val)
		if ($dc_val != null) {
			if (is_array ($dc_val))
				foreach ($dc_val as $dc_subval)
					$url2xpath = $url2xpath . create_xpath_predicate ($dc_key, $dc_subval);
			else
				$url2xpath = $url2xpath . create_xpath_predicate ($dc_key, $dc_val);
		}

	//$DEBUG = true;
	if ($DEBUG) {
		echo '<pre>';
		echo $url2xpath;
		echo '</pre>';
		die ();
	}

	/*
	 * Lettura indice, imposta shared lock.
	 */
	$index = fopen ($META_INDEX, 'r');
	if (flock ($index, LOCK_SH)) {

		/* Creazione DOM. */
		$dom_index = new DomDocument ("1.0","UTF-8");
		$dom_index->load ($META_INDEX);

		/* Creazione ed esecuzione XPath. */
		$xpath_index = new DomXPath ($dom_index);
		$results = $xpath_index->query ($url2xpath);
	}
	else {
		echo DSError ("500", "Errore di lettura dell'indice delle schede: impossibile acquisire l'accesso esclusivo.");
		die ();
	}

	fclose ($index);	// rilascia il lock automaticamente

	/*
	 * Creazione response.
	 */
	$dom_response = new DomDocument ("1.0","UTF-8");

	$el_response = $dom_response->createElementNS ("http://ltw.web.cs.unibo.it/resp",'ds:response');
	$dom_response->appendChild ($el_response);

	$at_query = $dom_response->createAttribute ('query');
	$at_query->value = "http://{$HOST}{$PATH}?" . htmlspecialchars ($QUERY);
	$el_response->setAttributeNode ($at_query);

	$attr=$dom_response->createAttributeNS("http://www.w3.org/2001/XMLSchema-instance",'xsi:schemaLocation');
	$attr->value= htmlentities("http://ltw.web.cs.unibo.it/resp http://vitali.web.cs.unibo.it/twiki/pub/TechWeb08/PagSchemaDS/responseSchema.xsd");
	$el_response->setAttributeNode($attr);

	/*
	 if(!$dom_response->schemaValidate($pathSchemaScheda)){
		$docRit=new DOMDocument();
		$risp=$docRit->createElement('risposta');
		$risp=$docRit->appendChild($risp);
		$err=error("500","La response non ha passato la validazione.");
		$err=DOMDocument::loadXML($err);
		$err=$err->getElementsByTagName('errore')->item(0);
		$err=$docRit->importNode($err,true);
		$risp->appendChild($err);
		print $docRit->saveXML();
		die;
	}
	*/

	foreach ($results as $el_metadati)
		$el_response->appendChild ($dom_response->importNode ($el_metadati, true));

	if (!@$dom_response->schemaValidate ("http://vitali.web.cs.unibo.it/twiki/pub/TechWeb08/PagSchemaDS/responseSchema.xsd")) {
		echo DSError ("500", "Il Data Source ha prodotto un response non valido.");
		die;
	}

	output_typed_string ($dom_response->saveXML(), "application/xml");
}


function create_xpath_predicate ($dc_key, $dc_val)
	/*
	 * Prende in input una coppia di valori chiave e valore come comparivano nella
	 * query string e ritorna il/i predicato/i xpath corrispondente/i.
	 */
{

	/*
	 * Caso senza wildcard.
	 */
	if (strpos ($dc_val, '*') === false)
		return "[.//{$dc_key}='{$dc_val}']";


	/*
	 * Caso con wilcard.
	 */
	// Collassa piu' asterischi consecutivi in uno solo.
	$dc_val = preg_replace ('%\*{2,}%', '*', $dc_val);

	$chunks = explode ('*', $dc_val);
	$len = count ($chunks);

	for ($i = 0; $i < $len; $i++) {
		// wtitle=geppo*...
		if ($i == 0 && $chunks[$i])
			$predicate = $predicate . "[.//{$dc_key} [starts-with (., '{$chunks[$i]}')]]";
		// wtitle=...*geppo
		if ($i == $len - 1 && $chunks[$i])
			// NB: Questa schifezza e' necessaria perche' in XPath 1.0 c'e' starts-with ma NON ends-with. Grazie w3c.
			$predicate = $predicate . "[.//{$dc_key} [substring (., string-length (.) - string-length ('{$chunks[$i]}') + 1, string-length ('{$chunks[$i]}')) = '{$chunks[$i]}']]";
		// wtitle=...*geppo*...
		if ($i > 0 && $i < $len - 1)
			$predicate = $predicate . "[.//{$dc_key} [contains (., '{$chunks[$i]}')]]";

	}
	return $predicate;
}


function save_expression ()
{
	include "postScheda.php5";
}

